package com.sardine.base.reflection;

import com.sardine.base.exception.SardineException;
import com.sardine.base.utils.DateUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.framework.AdvisedSupport;
import org.springframework.aop.framework.AopProxy;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ReflectionUtils;

import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * 北京京东世纪贸易有限公司<br>
 * Created with IntelliJ IDEA.
 * Date:2015/10/18
 * Time:12:06
 *
 * @author daifeng<br>
 *         daifeng@jd.com
 * @since JDK 1.6 <br>
 * Copyright (c) 2015 JD System Incorporated All Rights Reserved.
 */
public class BeanHelper extends BeanUtils {
    private static final Logger logger = LoggerFactory.getLogger(BeanHelper.class);

    /**
     * 将数组对象转换成指定类型的数组对象
     *
     * @param sourceList 原数组对象
     * @param target     要转换的目标数组对象类型
     * @param <T>
     * @return
     */
    public static <T> List<T> copyTo(Collection<?> sourceList, Class<T> target) {
        List<T> list = new ArrayList<T>();
        if (CollectionUtils.isEmpty(sourceList)) {
            return list;
//            return Collections.emptyList();// jsf调用saf服务时，该方法生成的数组反序列化会有问题 add by liuhuiqing
        }
        try {
            for (Object o : sourceList) {
                list.add(copyTo(o, target));
            }
        } catch (Exception e) {
            logger.error("数组复制出现错误source=[{}],targetType=[{}],errMsg=[{}]", new Object[]{sourceList, target, e.getMessage()});
            throw new SardineException(e.getMessage(),e,null,null);
        }
        return list;
    }

    /**
     * 对象类型转换
     *
     * @param sourceObj 原对象
     * @param target    要转换成的目标对象类型
     * @param <T>
     * @return
     */
    public static <T> T copyTo(Object sourceObj, Class<T> target) {
        if (sourceObj == null) {
            return null;
        }
        try {
            T e = target.newInstance();
            BeanUtils.copyProperties(sourceObj, e);
            return e;
        } catch (Exception e) {
            logger.error("对象复制出现错误source=[{}],targetType=[{}],errMsg=[{}]", new Object[]{sourceObj, target, e.getMessage()});
            throw new SardineException(e.getMessage(),e,null,null);
        }
    }

    /**
     * 将对象转换成map
     *
     * @param obj
     * @param prefix 生成map的key前缀
     * @param suffix 生成map的key后缀
     * @return Map<String,Object>
     * @author liuhuiqing
     * @date 2013-9-27 下午4:08:57
     */
    public static Map<String, Object> modelToMap(Object obj, String prefix, String suffix) {
        Map<String, Object> result = new HashMap<String, Object>();
        if(obj == null){
            return result;
        }
        Class<? extends Object> clazz = obj.getClass();
        for (; clazz != Object.class; clazz = clazz.getSuperclass()) {
            Field[] fs = clazz.getDeclaredFields();
            for (int i = 0; i < fs.length; i += 1) {
                if (Modifier.isStatic(fs[i].getModifiers())) {
                    continue;
                }
                boolean isAccessible = fs[i].isAccessible();
                if(!isAccessible){
                    fs[i].setAccessible(true);
                }
                try {
                    Object value = fs[i].get(obj);
                    String key = new StringBuffer(StringUtils.isBlank(prefix) ? ""
                            : prefix).append(fs[i].getName()).append(
                            StringUtils.isBlank(suffix) ? "" : suffix).toString();
                    if (value != null && !result.containsKey(key)) {
                        result.put(key, value);
                    }
                } catch (Exception e) {
                    logger.error("对象转map出现错误source=[{}],prefix=[{}],suffix=[{}],errMsg=[{}]", new Object[]{obj, prefix, suffix, e.getMessage()});
                    throw new SardineException(e.getMessage(),e,null,null);
                }
                if(!isAccessible){
                    fs[i].setAccessible(isAccessible);
                }
            }
        }
        return result;
    }

    /**
     * 将javabean对象转换成map<String,String>对象
     *
     * @param obj
     * @return
     */
    public static Map<String, String> modelToMap(Object obj) {
        Map<String, String> result = new HashMap<String, String>();
        if(obj == null){
            return result;
        }
        Class<? extends Object> clazz = obj.getClass();
        for (; clazz != Object.class; clazz = clazz.getSuperclass()) {
            Field[] fs = clazz.getDeclaredFields();
            for (int i = 0; i < fs.length; i += 1) {
                if (Modifier.isStatic(fs[i].getModifiers())) {
                    continue;
                }
                boolean isAccessible = fs[i].isAccessible();
                if(!isAccessible){
                    fs[i].setAccessible(true);
                }
                try {
                    Object value = fs[i].get(obj);
                    if (value != null) {
                        result.put(fs[i].getName(), String.valueOf(value));
                    }
                } catch (Exception e) {
                    logger.error("对象转map出现错误source=[{}],errMsg=[{}]", new Object[]{obj, e.getMessage()});
                    throw new SardineException(e.getMessage(),e,null,null);
                }
                if(!isAccessible){
                    fs[i].setAccessible(isAccessible);
                }
            }
        }
        return result;
    }
    public static <T> T mapToModel(Map<String, String> map, Class<T> clazz) {
        return mapToModel(map,clazz,null);
    }
    /**
     * 将map转换成指定类型的对象
     * @param map
     * @param clazz 类型必须有无参构造方法
     * @param excludes 不需要转换的字段集合
     * @return Object
     * @author liuhuiqing
     * @date 2013-9-27 下午4:09:23
     */
    public static <T> T mapToModel(Map<String, String> map, Class<T> clazz,String[] excludes) {
        try {
            Set<String> keySet = map.keySet();
            if(excludes!=null && excludes.length > 0){
                keySet.removeAll(Arrays.asList(excludes));
            }
            T result = clazz.newInstance();
            for (String attr : keySet) {
                Field field = null;
                try {
                    field = clazz.getDeclaredField(attr);
                } catch (NoSuchFieldException e) {
                    continue;
                }
                boolean isAccessible = field.isAccessible();
                if(!isAccessible){
                    field.setAccessible(true);
                }
                Class<?> typeClazz = field.getType();
                Object o = map.get(attr);
                if (o == null) continue;
                if (typeClazz.equals(String.class)) {
                    String s = o.toString();
                    field.set(result, s);
                } else if (typeClazz.equals(Integer.class)) {
                    field.set(result, Integer.parseInt(o.toString()));
                } else if (typeClazz.equals(Long.class)) {
                    field.set(result, Long.parseLong(o.toString()));
                } else if (typeClazz.equals(Date.class)) {
                    String dateType = DateUtils.getFormat(o.toString());
                    if (dateType != null) {
                        SimpleDateFormat sdf = new SimpleDateFormat(dateType);
                        Date date = sdf.parse(o.toString());
                        field.set(result, date);
                    }
                } else if (typeClazz.equals(Float.class)) {
                    field.set(result, Float.parseFloat(o.toString()));
                } else if (typeClazz.equals(Double.class)) {
                    field.set(result, Double.parseDouble(o.toString()));
                }
                if(!isAccessible){
                    field.setAccessible(isAccessible);
                }
            }
            return result;
        } catch (Exception e) {
            logger.error("map转对象出现错误source=[{}],targetType=[{}],errMsg=[{}]", new Object[]{map, clazz, e.getMessage()});
            throw new SardineException(e.getMessage(),e,null,null);
        }
    }

    /**
     * 得到指定类型的指定位置的泛型实参
     *
     * @param clazz
     * @param index
     * @param <T>
     * @return
     */
    public static <T> Class<T> findParameterizedType(Class<?> clazz, int index) {
        Type parameterizedType = clazz.getGenericSuperclass();
        if(parameterizedType == null){
            return null;
        }
        //CGLUB subclass target object(泛型在父类上)
        if (!(parameterizedType instanceof ParameterizedType)) {
            parameterizedType = clazz.getSuperclass().getGenericSuperclass();
        }
        if (!(parameterizedType instanceof ParameterizedType)) {
            return null;
        }
        Type[] actualTypeArguments = ((ParameterizedType) parameterizedType).getActualTypeArguments();
        if (actualTypeArguments == null || actualTypeArguments.length == 0 || index > actualTypeArguments.length) {
            return null;
        }
        return (Class<T>) actualTypeArguments[index];
    }

    /**
     * 对象拷贝(或直接用org.apache.commons.lang.SerializationUtils.clone();)
     *
     * @param obj
     * @param <T>
     * @return
     */
    public static <T extends Serializable> T clone(T obj) {
        // 拷贝产生的对象
        T clonedObj = null;
        try {
            // 读取对象字节数据
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(obj);
            oos.close();
            // 分配内存空间，写入原始对象，生成新对象
            ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bais);
            //返回新对象，并做类型转换
            clonedObj = (T) ois.readObject();
            ois.close();
        } catch (Exception e) {
            logger.error("数据复制出现错误source=[{}],errMsg=[{}]", new Object[]{obj, e.getMessage()});
            throw new SardineException(e.getMessage(),e,null,null);
        }
        return clonedObj;
    }

    /**
     * 获取 目标对象
     * @param proxyObject 代理对象
     * @return
     * @throws Exception
     */
    public static Object getTarget(Object proxyObject) throws Exception {

        if(!AopUtils.isAopProxy(proxyObject)) {
            return proxyObject;//不是代理对象
        }
        if(AopUtils.isJdkDynamicProxy(proxyObject)) {
            return getJdkDynamicProxyTargetObject(proxyObject);
        } else { //cglib
            return getCglibProxyTargetObject(proxyObject);
        }
    }

    /**
     * 从对象中取出指定字段属性的值
     * @param obj 查找对象
     * @param fieldName 对象属性名称
     * @param <T> 对象属性对应的值
     * @return
     * @throws IllegalAccessException
     */
    public static  <T> T getFieldValue(Object obj,String fieldName) throws IllegalAccessException {
        if(obj == null){
            return null;
        }
        Field field = ReflectionUtils.findField(obj.getClass(), fieldName);
        if(field == null){
            return null;
        }
        return (T) field.get(obj);
    }

    private static Object getCglibProxyTargetObject(Object proxy) throws Exception {
        Field h = proxy.getClass().getDeclaredField("CGLIB$CALLBACK_0");
        h.setAccessible(true);
        Object dynamicAdvisedInterceptor = h.get(proxy);
        Field advised = dynamicAdvisedInterceptor.getClass().getDeclaredField("advised");
        advised.setAccessible(true);
        Object target = ((AdvisedSupport)advised.get(dynamicAdvisedInterceptor)).getTargetSource().getTarget();

        return target;
    }

    private static Object getJdkDynamicProxyTargetObject(Object proxy) throws Exception {
        Field h = proxy.getClass().getSuperclass().getDeclaredField("h");
        h.setAccessible(true);
        AopProxy aopProxy = (AopProxy) h.get(proxy);
        Field advised = aopProxy.getClass().getDeclaredField("advised");
        advised.setAccessible(true);
        Object target = ((AdvisedSupport)advised.get(aopProxy)).getTargetSource().getTarget();
        return target;
    }


}
